/********************************************************
 Name          : xmodem.c
 Author        : Khachatur Gyozalyan
 Copyright     : Copyright by Antel Systems
 Description   :
 **********************************************************/
#include "xmodem.h"
#include "crc16_ccitt.h"
//#include "usb_if.h"
#include "CfgParams.h"
//#include "CLI.h"


/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       inbyte
  Description :       This function get one byte from usart_fast.
  Return      :       int - read byte or -2 if no data during timeout
  Parameters  :       int - timeout.
  Note        :
\*================================================================================*/
int inbyte(int timeout)
{
	int delay = timeout;
	unsigned char data;
	while(delay)
	{
		if(operation_mode.usart_port_mode == CMD_MODE)
		{
			if(usart_get_data(&data,1))
				return (int)data;
		}
/*		else if(operation_mode.usb_port_mode == CMD_MODE)
		{
			if(usb_get_data(&data,1))
				return (int)data;
khachik 	}*/
		delay--;
		delay_ms(1);
	}
	return -2;
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       flushinput
  Description :       This function flush the usart_fast buffer.
  Return      :       void
  Parameters  :       void
  Note        :
\*================================================================================*/
static void flushinput(void)
{
	while (inbyte(((DLY_500ms)*3)>>1) >= 0)
		;
}


unsigned char flush_buffer[AT25DFX_SECTOR_SIZE];
unsigned int flush_buffer_index = 0;
unsigned int sector_address;
/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       read_from_flash
  Description :       This function read data from falsh.
  Return      :       void
  Parameters  :       unsigned char * - data
                      int - length
                      int - address
  Note        :
\*================================================================================*/
void read_from_flash(unsigned char *data, int length, unsigned int address)
{
/*	unsigned int sector = sector_address + address / AT25DFX_SECTOR_SIZE;
	unsigned char buf[AT25DFX_SECTOR_SIZE];
	at25dfx_read_sector(sector,1,buf);
	memcpy(data,buf + address % AT25DFX_SECTOR_SIZE,length);*/
	at25dfx_read_bytes(address + (sector_address << AT25DFX_SECTOR_BITS),length,data);
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       write_to_flash
  Description :       This function add data to flush_buffer, and write it to the
                      flash when the buffer is full.
  Return      :       void
  Parameters  :       unsigned char * - data to write
                      int - length
  Note        :
\*================================================================================*/
void write_to_flash(unsigned char *data, int length)
{
	while(length)
	{
		flush_buffer[flush_buffer_index] = *data;
		flush_buffer_index++;
		if(flush_buffer_index == AT25DFX_SECTOR_SIZE)
		{
			at25dfx_write_sector(sector_address,1,flush_buffer);
			sector_address++;
			flush_buffer_index = 0;
		}

		data++;
		length--;
	}
}

void flash_cache(void)
{
	if(flush_buffer_index == 0)
		return;
	int i;
	for(i = flush_buffer_index ; i < AT25DFX_SECTOR_SIZE ; i++)
		flush_buffer[i] = 0x00;
	at25dfx_write_sector(sector_address,1,flush_buffer);
	flush_buffer_index = 0;
}

void xmod_print_char(unsigned char letter)
{
	if(operation_mode.usart_port_mode == CMD_MODE)
	{
		usart_print_char(letter);
	}
/*	else if(operation_mode.usb_port_mode == CMD_MODE)
	{
	khachik	usb_put_data(&letter,1);
	}*/
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       check
  Description :       This function check the CRC or checksum.
  Return      :       int - 1 if success, 0 if error
  Parameters  :       int - crc
                      const unsigned char * - buf
                      int - sz
  Note        :
\*================================================================================*/
static int check(int crc, const unsigned char *buf, int sz)
{
	if (crc) {
		unsigned short crc = crc16_ccitt(buf, sz);
		unsigned short tcrc = (buf[sz]<<8)+buf[sz+1];
		if (crc == tcrc)
			return 1;
	}
	else {
		int i;
		unsigned char cks = 0;
		for (i = 0; i < sz; ++i) {
			cks += buf[i];
		}
		if (cks == buf[sz])
		return 1;
	}

	return 0;
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       xmodemReceive
  Description :       This function receive file and store it in to the flash.
  Return      :       int - number of bytes downloaded, or error code
  Parameters  :       file - file to receive.
  Note        :
\*================================================================================*/
int xmodemReceive(struct file_t file)
{
	unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */
	unsigned char *p;
	int bufsz, crc = 0;
	unsigned char trychar = 'C';
	unsigned char packetno = 1;
	unsigned char first_packet = 1,correct_file = 1;
	int i, c, len = 0;
	int retry, retrans = MAXRETRANS;
	flush_buffer_index = 0;
	sector_address = file.sector_number;
	int destsz = file.max_size;
	for(;;)
	{
		for( retry = 0; retry < XMOD_MAX_RETRY; ++retry)
		{
			if (trychar)
				xmod_print_char(trychar);
			if ((c = inbyte((DLY_500ms)<<1)) >= 0)
			{
				switch (c)
				{
				case SOH:
					bufsz = 128;
					goto start_recv;
				case STX:
					bufsz = 1024;
					goto start_recv;
				case EOT:

					flushinput();
					xmod_print_char(ACK);
					flash_cache();
					if(!correct_file)
						return -4; /* Incorrect file */
					return len; /* normal end */
				case CAN:
					if ((c = inbyte(DLY_500ms)) == CAN)
					{
						flushinput();
						xmod_print_char(ACK);
						return -1; /* canceled by remote */
					}
					break;
				default:
					break;
				}
			}
		}
		if (trychar == 'C')
		{
			trychar = NAK;
			continue;
		}
		flushinput();
		xmod_print_char(CAN);
		xmod_print_char(CAN);
		xmod_print_char(CAN);
		return -2; /* sync error */

	start_recv:
		if (trychar == 'C')
			crc = 1;
		trychar = 0;
		p = xbuff;
		*p++ = c;
		for (i = 0;  i < (bufsz+(crc?1:0)+3); ++i)
		{
			if ((c = inbyte(DLY_500ms)) < 0)
				goto reject;
			*p++ = c;
		}

		if (xbuff[1] == (unsigned char)(~xbuff[2]) && 
			(xbuff[1] == packetno || xbuff[1] == (unsigned char)packetno-1) &&
			check(crc, &xbuff[3], bufsz))
		{
			if (xbuff[1] == packetno)
			{
				register int count = destsz - len;
				if (count > bufsz)
					count = bufsz;
				if(first_packet)
				{
					if(file.file_type == IMAGE_FILE_TYPE)
					{
						correct_file = 1;
						at25dfx_erase(file.sector_number,(file.max_size) >> AT25DFX_SECTOR_BITS);
					}
					else
					{
						if(check_file_header(&(xbuff[3]),file))
						{
							correct_file = 1;
							at25dfx_erase(file.sector_number,(file.max_size) >> AT25DFX_SECTOR_BITS);
						}
						else
						{
							correct_file = 0;
						}
					}
					first_packet = 0;
				}
				if (count > 0)
				{
					//memcpy(&dest[len], &xbuff[3], count);
					if(correct_file)
						write_to_flash(&xbuff[3], count);
					len += count;
				}
				++packetno;
				retrans = MAXRETRANS+1;
			}
			if (--retrans <= 0)
			{
				flushinput();
				xmod_print_char(CAN);
				xmod_print_char(CAN);
				xmod_print_char(CAN);
				return -3; /* too many retry error */
			}
			xmod_print_char(ACK);
			continue;
		}
	reject:
		flushinput();
		xmod_print_char(NAK);
	}
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       xmodemTransmit
  Description :       This function transmit from the flash to the terminal.
  Return      :       int - number of bytes transmited, or error code
  Parameters  :       file - file to transmit.
  Note        :
\*================================================================================*/
int xmodemTransmit(struct file_t file)
{
	unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */
	int bufsz, crc = -1;
	unsigned char packetno = 1;
	int i, c, len = 0;
	int retry;
	sector_address = file.sector_number;
	int destsz = file.max_size;
	for(;;)
	{
		for( retry = 0; retry < XMOD_MAX_RETRY; ++retry)
		{
			if ((c = inbyte((DLY_500ms)<<1)) >= 0)
			{
				switch (c)
				{
				case 'C':
					crc = 1;
					goto start_trans;
				case NAK:
					crc = 0;
					goto start_trans;
				case CAN:
					if ((c = inbyte(DLY_500ms)) == CAN)
					{
						xmod_print_char(ACK);
						flushinput();
						return -1; /* canceled by remote */
					}
					break;
				default:
					break;
				}
			}
		}
		xmod_print_char(CAN);
		xmod_print_char(CAN);
		xmod_print_char(CAN);
		flushinput();
		return -2; /* no sync */

		for(;;)
		{
		start_trans:
			xbuff[0] = SOH; bufsz = 128;
			xbuff[1] = packetno;
			xbuff[2] = ~packetno;
			c = destsz - len;
			if (c > bufsz) c = bufsz;
			if (c >= 0)
			{
				memset (&xbuff[3], 0, bufsz);
				if (c == 0)
				{
					xbuff[3] = CTRLZ;
				}
				else
				{
					//memcpy (&xbuff[3], &src[len], c);
					read_from_flash(&xbuff[3],c,len);
					if (c < bufsz) xbuff[3+c] = CTRLZ;
				}
				if (crc)
				{
					unsigned short ccrc = crc16_ccitt(&xbuff[3], bufsz);
					xbuff[bufsz+3] = (ccrc>>8) & 0xFF;
					xbuff[bufsz+4] = ccrc & 0xFF;
				}
				else
				{
					unsigned char ccks = 0;
					for (i = 3; i < bufsz+3; ++i)
					{
						ccks += xbuff[i];
					}
					xbuff[bufsz+3] = ccks;
				}
				for (retry = 0; retry < MAXRETRANS; ++retry)
				{
					for (i = 0; i < bufsz+4+(crc?1:0); ++i)
					{
						xmod_print_char(xbuff[i]);
					}
					if ((c = inbyte(DLY_500ms)) >= 0 )
					{
						switch (c)
						{
						case ACK:
							++packetno;
							len += bufsz;
							goto start_trans;
						case CAN:
							if ((c = inbyte(DLY_500ms)) == CAN)
							{
								xmod_print_char(ACK);
								flushinput();
								return -1; /* canceled by remote */
							}
							break;
						case NAK:
						default:
							break;
						}
					}
				}
				xmod_print_char(CAN);
				xmod_print_char(CAN);
				xmod_print_char(CAN);
				flushinput();
				return -4; /* xmit error */
			}
			else
			{
				for (retry = 0; retry < 10; ++retry)
				{
					xmod_print_char(EOT);
					if ((c = inbyte((DLY_500ms)<<1)) == ACK) break;
				}
				flushinput();
				return (c == ACK)?len:-5;
			}
		}
	}
}


